//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections;
using System.Collections.Generic;

namespace OpenLS.Spreadsheet
{
    class WeakDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>> where TValue : IWeakable
        {
            class Holder
            {
                public Holder(WeakDictionary<TKey, TValue> dictionary, TValue value, TKey key)
                {
                    Dictionary = dictionary;
                    Value = value;
                    Key = key;
                    Value.WeakHolder = this;
                }
                ~Holder()
                {
                    Dictionary.RemoveWeak(this);
                }

                private readonly WeakDictionary<TKey, TValue> Dictionary;
                public TValue Value { get; set; }
                public TKey Key { get; set; }
            }

            public int Count
            {
                get
                {
                    lock (this)
                    {
                        return Inner.Count;
                    }
                }
            }
            public IEnumerable<TKey> Keys
            {
                get
                {
                    lock (Inner)
                    {
                        foreach (var v in Inner)
                        {
                            WeakReference reference = v.Value;
                            if (reference.IsAlive)
                                yield return v.Key;
                        }
                    }
                }
            }
            public IEnumerable<TValue> Values
            {
                get
                {
                    lock (Inner)
                    {
                        foreach (var v in Inner)
                        {
                            WeakReference reference = v.Value;
                            if (reference.IsAlive)
                                yield return ((Holder)reference.Target).Value;
                        }
                    }
                }
            }
            readonly Dictionary<TKey, WeakReference> _inner = new Dictionary<TKey, WeakReference>();
        Dictionary<TKey, WeakReference> Inner
        {
            get { return _inner; }
        }
            public void Add(TKey key, TValue value)
            {
                lock (Inner)
                {
                    var v = new Holder(this, value, key);
                    Inner.Add(key, new WeakReference(v));
                }
            }
            public void Remove(TKey key)
            {
                lock (Inner)
                {
                    Inner.Remove(key);
                }
            }
            public bool ContainsKey(TKey key)
            {
                lock (Inner)
                {
                    WeakReference wr;
                    if (!Inner.TryGetValue(key, out wr))
                        return false;
                    return wr.IsAlive;
                }
            }
            public TValue this[TKey key]
            {
                get
                {
                    lock (Inner)
                    {
                        WeakReference wr = Inner[key];
                        if (wr.IsAlive)
                        {
                            var holder = (Holder)wr.Target;
                            return holder.Value;
                        }
                        throw new ArgumentException();
                    }
                }
            }

            private void RemoveWeak(Holder holder)
            {
                lock (Inner)
                {
                    WeakReference wr;
                    if (!Inner.TryGetValue(holder.Key, out wr))
                        return;
                    if (wr.IsAlive)
                        return;
                    Inner.Remove(holder.Key);
                }
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return ((IEnumerable<KeyValuePair<TKey, TValue>>)this).GetEnumerator();
            }

            public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
            {
                lock (Inner)
                {
                    foreach (var v in Inner)
                    {
                        WeakReference reference = v.Value;
                        if (reference.IsAlive)
                            yield return
                                new KeyValuePair<TKey, TValue>(v.Key, ((Holder)reference.Target).Value);
                    }
                }

            }

            internal bool TryGetValue(TKey key, out TValue result)
            {
                lock (Inner)
                {
                    WeakReference wr;
                    if (!Inner.TryGetValue(key, out wr))
                    {
                        result = default(TValue);
                        return false;
                    }
                    var target = wr.Target as Holder;
                    if (target == null)
                    {
                        result = default(TValue);
                        Inner.Remove(key);
                        return false;
                    }
                    result =  target.Value;
                    return true;
                }
            }
        }


    }